package worker

import (
	"crontab/common"
	"fmt"
	"time"
)

type Scheduler struct {
	jobEventChan chan *common.JobEvent
	jobPlanTable map[string]*common.JobSchedulePlan
}

var (
	G_scheduler *Scheduler
)

// 初始化调度器
func InitScheduler() error {
	G_scheduler = &Scheduler{
		jobEventChan: make(chan *common.JobEvent, 1000),
		jobPlanTable: make(map[string]*common.JobSchedulePlan),
	}
	go G_scheduler.scheduleLoop()
	return nil
}

func (s *Scheduler) scheduleLoop() {
	scheduleAfter := s.TrySchedule()
	scheduleTimer := time.NewTimer(scheduleAfter)
	for {
		select {
		case jobEvent = <-s.jobEventChan:
			s.handleJobEvent(jobEvent)
		case <-scheduleTimer.C:
		}
		scheduleAfter = s.TrySchedule()
		scheduleTimer.Reset(scheduleAfter)
	}

}

func (s *Scheduler) PushJobEvent(jobEvent *common.JobEvent) {
	s.jobEventChan <- jobEvent
}

func (s *Scheduler) handleJobEvent(jobEvent *common.JobEvent) {
	switch jobEvent.EventType {
	case common.JOB_EVENT_SAVE:
		jobSchedulePlan, err := common.BuildJobSchedulePlan(jobEvent.Job)
		if err != nil {
			return
		}
		s.jobPlanTable[jobEvent.Job.Name] = jobSchedulePlan
	case common.JOB_EVENT_DELETE:
		delete(s.jobPlanTable, jobEvent.Job.Name)
	}
}

func (s *Scheduler) TrySchedule() (scheduleAfter time.Duration) {
	var nearTime *time.Time
	now := time.Now()
	if len(s.jobPlanTable) == 0 {
		scheduleAfter = 1 * time.Second
	}
	for _, jobPlan := range s.jobPlanTable {
		if jobPlan.NextTime.Before(now) || jobPlan.NextTime.Equal(now) {
			fmt.Println("执行任务", jobPlan.Job.Name)
			jobPlan.NextTime = jobPlan.Expr.Next(now)
		}
		if nearTime == nil || jobPlan.NextTime.Before(*nearTime) {
			nearTime = &jobPlan.NextTime
		}
	}
	scheduleAfter = nearTime.Sub(now)
	return
}
